using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace SavedVariablesParser.Line {
    /// <summary>
    /// Provides functionality to create <see cref="ILine"/> objects.
    /// </summary>
    public static class LineFactory {
        /// <summary>
        /// Creates an <see cref="ILine"/> object based on the line specified.
        /// </summary>
        /// <param name="line">The line to create an <see cref="ILine"/> object for.</param>
        /// <param name="lineNumber">The position of the line within the originating file.</param>
        /// <returns>An <see cref="ILine"/> object that is used to represent the line specified.</returns>
        /// <exception cref="ArgumentNullException">
        /// 	<para>The argument <paramref name="line"/> is <langword name="null"/>.</para>
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// 	<para>The argument <paramref name="line"/> is out of range.</para>
        /// </exception>
        public static ILine CreateLine(string line, int lineNumber) {            
            if (line == null) {
                throw new ArgumentNullException("line", "The value of 'line' cannot be null.");
            }
            if (line.Length <= 0) {
                throw new ArgumentOutOfRangeException("line", "The value of 'line' cannot be a zero length string.");
            }
            if (lineNumber <= 0) {
                throw new ArgumentOutOfRangeException("line", "The value of 'lineNumber' must be greater than zero.");
            }

            ILine returnLine = null;

            // determine the type of line, and depending on that type create an appropriate ILine object.
            switch (GetLineType(line)) {
                case LineType.BeginIndex:
                    returnLine = new BeginIndex(line, LineType.BeginIndex, lineNumber);
                    break;
                case LineType.EndIndex:
                    returnLine = new EndIndex(line, LineType.EndIndex, lineNumber);
                    break;
                case LineType.BeginArray:
                    returnLine = new BeginArray(line, LineType.BeginArray, lineNumber);
                    break;
                case LineType.EndArray:
                    returnLine = new EndArray(line, LineType.EndArray, lineNumber);
                    break;
                case LineType.BooleanValue:
                    returnLine = new DataLine(line, LineType.BooleanValue, lineNumber);
                    break;
                case LineType.NumericValue:
                    returnLine = new DataLine(line, LineType.NumericValue, lineNumber);
                    break;
                case LineType.StringValue:
                    returnLine = new DataLine(line, LineType.StringValue, lineNumber);
                    break;
                case LineType.OutOfArrayValue:
                    returnLine = new DataLine(line, LineType.OutOfArrayValue, lineNumber);
                    break;
                case LineType.EmptyLine:
                    returnLine = new EmptyLine(line, LineType.EmptyLine, lineNumber);
                    break;
                case LineType.NotSupported:
                    throw new LineParserException(line, lineNumber);
                default:
                    throw new LineParserException(line, lineNumber);
            }

            return returnLine;
        }

        /// <summary>
        /// Determines the type of line that was passed into the factory.
        /// </summary>
        /// <param name="line">The line passed into the factory.</param>
        /// <returns>The <see cref="LineType"/> that the line is.</returns>
        private static LineType GetLineType(string line) {
            string trimmedLine = line.Replace("\t", string.Empty).Trim();// trim tabs and spaces off the beginning and end of line to make the following methods more optimized.

            #region beta 

            //TODO:  comment the bajesuz out of this order of operations

            if (IsStringValueLine(trimmedLine)) {
                return LineType.StringValue;
            } else if (IsNumericValueLine(trimmedLine)) {
                return LineType.NumericValue;
            } else if (IsBooleanValueLine(trimmedLine)) {
                return LineType.BooleanValue;
            } else if (IsBeginningOfIndexLine(trimmedLine)) {
                return LineType.BeginIndex;
            } else if (IsEndOfIndexLine(trimmedLine)) {
                return LineType.EndIndex;
            } else if (IsBeginningOfArrayLine(trimmedLine)) {
                return LineType.BeginArray;
            } else if (IsEndOfArrayLine(trimmedLine)) {
                return LineType.EndArray;
            } else if (IsValueWithoutArrayLine(trimmedLine)) {
                return LineType.OutOfArrayValue;
            }else if(IsEmptyLine(trimmedLine)){
                return LineType.EmptyLine;
            } else {
                return LineType.NotSupported;
            }

            #endregion


            #region this works awesome but is slow  12.5 second average for auctioneer

            //if (IsBeginningOfIndexLine(trimmedLine)) {
            //    return LineType.BeginIndex;
            //} else if (IsBeginningOfArrayLine(trimmedLine)) {
            //    return LineType.BeginArray;
            //} else if (IsEndOfIndexLine(trimmedLine)) {
            //    return LineType.EndIndex;
            //} else if (IsEndOfArrayLine(trimmedLine)) {
            //    return LineType.EndArray;
            //} else if (IsStringValueLine(trimmedLine)) {
            //    return LineType.StringValue;
            //} else if (IsNumericValueLine(trimmedLine)) {
            //    return LineType.NumericValue;
            //} else if (IsBooleanValueLine(trimmedLine)) {
            //    return LineType.BooleanValue;
            //} else if (IsValueWithoutArrayLine(trimmedLine)) {
            //    return LineType.OutOfArrayValue;
            //} else {
            //    return LineType.NotSupported;
            //}

            #endregion
        }

        /// <summary>
        /// Determines if the line is a beginning of index line.
        /// </summary>
        /// <param name="line">The line to test.</param>
        /// <returns>true if the line is a beginning of index line, false if not.</returns>
        private static bool IsBeginningOfIndexLine(string line) {
            // ["somevalue"] = {
            ////Regex indexRegularExpression = new Regex(@"\u005B(.*)\u005D\s=\s\u007B", RegexOptions.IgnoreCase); // done!
            //return indexRegularExpression.IsMatch(line);

            if (line.StartsWith("[", StringComparison.CurrentCulture) && line.EndsWith("{")) {
                return true;
            } else {
                return false;
            }
        }

        /// <summary>
        /// Determines if the line is a beginning of array line.
        /// </summary>
        /// <param name="line">The line to test.</param>
        /// <returns>true if the line is a beginning of array line, false if not.</returns>
        private static bool IsBeginningOfArrayLine(string line) {
            //// word = {
            ////Regex arrayRegularExpression = new Regex(@"(.*)\s=\s\u007B", RegexOptions.IgnoreCase); // done!
            //return arrayRegularExpression.IsMatch(line);
            return line.EndsWith("{");
        }

        /// <summary>
        /// Determines if the line is a end of array line.
        /// </summary>
        /// <param name="line">The line to test.</param>
        /// <returns>true if the line is a end of array line, false if not.</returns>
        private static bool IsEndOfArrayLine(string line) {
            //// }
            ////Regex endArrayRegularExpression = new Regex(@"\u007D", RegexOptions.IgnoreCase); // done!
            //return endArrayRegularExpression.IsMatch(line);
            return line.EndsWith("}");
        }

        /// <summary>
        /// Determines if the line is a end of index line.
        /// </summary>
        /// <param name="line">The line to test.</param>
        /// <returns>true if the line is a end of index line, false if not.</returns>
        private static bool IsEndOfIndexLine(string line) {
            // },
            ////Regex endIndexRegularExpression = new Regex(@"\u007D\u002C", RegexOptions.IgnoreCase); // done!
            //return endIndexRegularExpression.IsMatch(line);
            return line.EndsWith("},");
        }

        /// <summary>
        /// Determines if the line is a numeric value line.
        /// </summary>
        /// <param name="line">The line to test.</param>
        /// <returns>true if the line is a numeric value line, false if not.</returns>
        private static bool IsNumericValueLine(string line) {
            // [dataname] = value,
            ////Regex numericValueRegularExpression = new Regex(@"\u005B(.*)\u005D\s=\s[0-9]+", RegexOptions.IgnoreCase); // done!
            //return numericValueRegularExpression.IsMatch(line);
            if (line.StartsWith("[", StringComparison.CurrentCulture) && line.EndsWith(",")) {
                return true;
            } else {
                return false;
            }
        }

        /// <summary>
        /// Determines if the line is a string value line.
        /// </summary>
        /// <param name="line">The line to test.</param>
        /// <returns>true if the line is a string value line, false if not.</returns>
        private static bool IsStringValueLine(string line) {
            //Regex stringValueRegularExpression = new Regex(@"\u005B(.*)\u005D\s=\s\u0022(.*)\u0022", RegexOptions.IgnoreCase); // done!
            //return stringValueRegularExpression.IsMatch(line);
            if (line.StartsWith("[", StringComparison.CurrentCulture) && line.EndsWith(",")) {
                return true;
            } else {
                return false;
            }
        }

        /// <summary>
        /// Determines if the line is a boolean value line.
        /// </summary>
        /// <param name="line">The line to test.</param>
        /// <returns>true if the line is a boolean value line, false if not.</returns>
        private static bool IsBooleanValueLine(string line) {
            //Regex booleanValueRegularExpression = new Regex(@"\u005B(.*)\u005D\s=\s(true|false)", RegexOptions.IgnoreCase);
            //return booleanValueRegularExpression.IsMatch(line);
            if (line.StartsWith("[", StringComparison.CurrentCulture) && line.EndsWith(",")) {
                return true;
            } else {
                return false;
            }
        }

        /// <summary>
        /// Determines if the line is a value that doesnt belong to an array line.
        /// </summary>
        /// <param name="line">The line to test.</param>
        /// <returns>true if the line is a value that doesnt belong to an array line, false if not.</returns>
        private static bool IsValueWithoutArrayLine(string line) {
            Regex valueNoArrayRegularExpression = new Regex(@"(.*)\s=\s(.*)", RegexOptions.IgnoreCase); // done!;
            return valueNoArrayRegularExpression.IsMatch(line);
            //if (line.StartsWith("[", StringComparison.CurrentCulture) && line.EndsWith("")) {
            //    return true;
            //} else {
            //    return false;
            //}
        }

        /// <summary>
        /// Determines if the line is empty, containing no data or a carriage return / line feed.
        /// </summary>
        /// <param name="line">The line to test.</param>
        /// <returns>true if the line is a value that contains no data, false if not.</returns>
        private static bool IsEmptyLine(string line) {
            if (line.Equals(string.Empty) || line.Equals("\\r") || line.Equals("\\n") || line.Equals(Environment.NewLine)) {
                return true;
            }
            return false;
        }
    }
}